package com.example.system.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.core.enums.ResultCode;
import com.example.security.exception.ServiceException;
import com.example.system.entity.DO.ExamDO;
import com.example.system.entity.DO.ExamQuestionDO;
import com.example.system.entity.DO.QuestionDO;
import com.example.system.entity.DTO.ExamAddDTO;
import com.example.system.entity.DTO.ExamEditDTO;
import com.example.system.entity.DTO.ExamQueryDTO;
import com.example.system.entity.DTO.ExamQuestAddDTO;
import com.example.system.entity.VO.ExamDetailVO;
import com.example.system.entity.VO.ExamVO;
import com.example.system.entity.VO.QuestionVO;
import com.example.system.enums.ExamStatus;
import com.example.system.component.manager.ExamCacheManager;
import com.example.system.mapper.ExamMapper;
import com.example.system.service.IExamQuestionService;
import com.example.system.service.IExamService;
import com.example.system.service.IQuestionService;
import com.github.pagehelper.PageHelper;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

@Service
public class ExamServiceImpl implements IExamService {
    @Autowired
    private ExamMapper examMapper;
    @Autowired
    private IQuestionService questionService;
    @Autowired
    private IExamQuestionService examQuestionService;
    @Autowired
    private ExamCacheManager examCacheManager;

    @Override
    public List<ExamVO> list(ExamQueryDTO examQueryDTO) {
        PageHelper.startPage(examQueryDTO.getPageNum(), examQueryDTO.getPageSize());
        return examMapper.selectExamList(examQueryDTO);
    }

    @Override
    public String add(ExamAddDTO examAddDTO) {
        // 检验参数
        ExamDO examDO = new ExamDO();
        BeanUtils.copyProperties(examAddDTO, examDO);
        this.checkExamSaveParams(examDO, null);
        int insert = examMapper.insert(examDO);
        if (insert != 1) {
            throw new ServiceException(ResultCode.FAILED);
        }
        return examDO.getExamId().toString();
    }


    @Override
    public boolean questionAdd(ExamQuestAddDTO examQuestAddDTO) {
        // 校验参数
        this.checkQuestionSaveParams(examQuestAddDTO);
        if (examQuestAddDTO.getQuestionIdSet().isEmpty()) {
            return true;
        }

        List<Long> questionIds = examQuestAddDTO.getQuestionIdSet();
        List<QuestionDO> questionList = questionService.getListByIds(questionIds);
        if (CollectionUtil.isEmpty(questionList) || questionList.size() < questionIds.size()) {
            throw new ServiceException(ResultCode.EXAM_QUESTION_NOT_EXISTS);
        }
        List<ExamQuestionDO> examQuestionList = new ArrayList<>();
        int count = 1;
        for (Long questionId : questionIds) {
            ExamQuestionDO examQuestion = new ExamQuestionDO();
            examQuestion.setExamId(examQuestAddDTO.getExamId());
            examQuestion.setQuestionId(questionId);
            examQuestion.setQuestionOrder(count++);
            examQuestionList.add(examQuestion);
        }
        return examQuestionService.saveQuestion(examQuestionList);
    }


    @Override
    public ExamDetailVO detail(Long examId) {
        ExamDO examDO = this.checkExamId(examId);
        List<QuestionVO> questionList = examQuestionService.selectExamQuestionList(examId);
        ExamDetailVO examDetailVO = new ExamDetailVO();
        BeanUtils.copyProperties(examDO, examDetailVO);
        examDetailVO.setExamQuestionList(questionList);

        return examDetailVO;
    }

    @Override
    public int edit(ExamEditDTO examEditDTO) {
        ExamDO examDO = this.checkExamId(examEditDTO.getExamId());
        if (examDO.getStatus().equals(ExamStatus.PUBLISHED.getStatus())) {
            throw new ServiceException(ResultCode.EXAM_IS_PUBLISH);
        }

        BeanUtils.copyProperties(examEditDTO, examDO);
        this.checkExamSaveParams(examDO, examDO.getExamId());

        return examMapper.updateById(examDO);
    }


    @Override
    public int questionDelete(Long examId, Long questionId) {
        ExamDO examDO = this.checkExamId(examId);
        this.checkExamTime(examDO.getStartTime());
        if (examDO.getStatus().equals(ExamStatus.PUBLISHED.getStatus())) {
            throw new ServiceException(ResultCode.EXAM_IS_PUBLISH);
        }

        return examQuestionService.questionDelete(examId, questionId);
    }

    @Override
    @Transactional
    public int delete(Long examId) {
        ExamDO examDO = this.checkExamId(examId);
        if (examDO.getStatus().equals(ExamStatus.PUBLISHED.getStatus())) {
            throw new ServiceException(ResultCode.EXAM_IS_PUBLISH);
        }

        examQuestionService.deleteByExamId(examId);
        return examMapper.deleteById(examId);
    }


    @Override
    public int publish(Long examId) {
        ExamDO exam = this.checkExamId(examId);
        this.checkExamTime(exam.getStartTime());
        // 竞赛中不包含题目则不允许发布
        Long count = examQuestionService.selectQuestionCount(examId);
        if (count == null || count <= 0) {
            throw new ServiceException(ResultCode.EXAM_NOT_HAS_QUESTION);
        }
        exam.setStatus(ExamStatus.PUBLISHED.getStatus());
        // 把发布的竞赛添加到redis中
        examCacheManager.addCache(exam);
        return examMapper.updateById(exam);
    }

    @Override
    public int cancelPublish(Long examId) {
        ExamDO examDO = this.checkExamId(examId);
        this.checkExamTime(examDO.getStartTime());
        examDO.setStatus(ExamStatus.UN_PUBLISH.getStatus());
        // 删掉redis中的数据
        examCacheManager.deleteCache(examId);
        return examMapper.updateById(examDO);
    }

    /**
     * 校验 questionAdd参数
     * @param examQuestAddDTO examQuestAddDTO
     */
    private void checkQuestionSaveParams(ExamQuestAddDTO examQuestAddDTO) {
        // 校验id
        ExamDO examDO = this.checkExamId(examQuestAddDTO.getExamId());
        // 检验时间
        this.checkExamTime(examDO.getStartTime());
        // 去除竞赛中的重复题目
        List<Long> questionIds = examQuestionService.
                removeDuplication(examQuestAddDTO.getExamId(), examQuestAddDTO.getQuestionIdSet());
        examQuestAddDTO.setQuestionIdSet(questionIds);
    }

    /**
     * 校验 add参数
     *
     * @param examSaveDO examSaveDTO
     * @param examId     examId
     */
    private void checkExamSaveParams(ExamDO examSaveDO, Long examId) {
        // 竞赛开始、结束时间进行判断
        this.checkExamTime(examSaveDO.getStartTime());
        // 竞赛标题是否重复进行判断
        LambdaQueryWrapper<ExamDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ExamDO::getTitle, examSaveDO.getTitle())
                .ne(examId != null, ExamDO::getExamId, examId);
        ExamDO exists = examMapper.selectOne(wrapper);
        if (exists != null) {
            throw new ServiceException(ResultCode.FAILED_ALREADY_EXISTS);
        }
    }

    /**
     * 校验竞赛时间
     * @param examStartTime 竞赛开始时间
     */
    private void checkExamTime(LocalDateTime examStartTime) {
        if (examStartTime.isBefore(LocalDateTime.now())) {
            throw new ServiceException(ResultCode.EXAM_START_TIME_BEFORE_CURRENT_TIME);
        }
    }

    /**
     * 校验examId，并返回
     * @param examId examId
     * @return ExamDO
     */
    private ExamDO checkExamId(Long examId) {
        ExamDO examDO = examMapper.selectById(examId);
        if (examDO == null) {
            throw new ServiceException(ResultCode.FAILED_NOT_EXISTS);
        }
        return examDO;
    }



}
